package com.eclipse.org.snms.serializer.storageAccess;
import com.eclipse.org.snms.StickyNote;
import com.eclipse.org.snms.TaskComposite;
import com.eclipse.org.snms.TaskStep;
import com.eclipse.org.snms.serializer.stickyNoteList.*;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer; //import java.util.Calendar;
//import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Stack;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;


/**
 * 
 * @author toufic
 *
 */
public class StorageAccess {
	
	private static StorageAccess uniqueInstance;
	
	private String xmlFile="StickyNotes.xml";
	private File file=new File(xmlFile);

	private StickyNoteList stickyNoteList = StickyNoteList.getUniqueInstance();
	Stack<Object> stack = new Stack<Object>();
	
	private StorageAccess(){}
	
	public static synchronized StorageAccess getUniqueInstance(){
		if(uniqueInstance == null){
			uniqueInstance = new StorageAccess();
		}
		return uniqueInstance;
	}
	
	public void setFile(String xmlFile) {
		this.xmlFile = xmlFile;
	}

	public void readXML() throws ParseException, IOException {
		if (file == null) {
			throw new IllegalArgumentException("File should not be null.");
		}
		if (!file.exists()) {
			throw new FileNotFoundException("File does not exist: " + file);
		}
		if (!file.isFile()) {
			throw new IllegalArgumentException("Should not be a directory: "
					+ file);
		}
		if (!file.canWrite()) {
			throw new IllegalArgumentException("File cannot be written: "
					+ file);
		}
		try {
			// First create a new XMLInputFactory
			XMLInputFactory inputFactory = XMLInputFactory.newInstance();
			// Setup a new eventReader
			InputStream in = new FileInputStream(file);
			XMLEventReader eventReader = inputFactory.createXMLEventReader(in);
			// Read the XML document
			while (eventReader.hasNext()) {
				XMLEvent event = eventReader.nextEvent();

				if (event.isStartElement()) {
					if (event.asStartElement().getName().getLocalPart() == ("task")) {
						// Create TaskComposite object
						TaskComposite taskComposite = new TaskComposite();
						if (!stack.isEmpty()) {
							// Pop task from stack
							TaskComposite task = (TaskComposite) stack.peek();
							// Add task step to the task
							task.add(taskComposite);
						}
						// Push it to the stack
						stack.push(taskComposite);
						event = eventReader.nextEvent();
						continue;
					}
					if (event.asStartElement().getName().getLocalPart() == ("task-step")) {
						// Create TaskStep object
						TaskStep taskStep = new TaskStep();
						// Pop task from stack
						TaskComposite task = (TaskComposite) stack.peek();
						// Add task step to the task
						task.add(taskStep);
						// Push TaskStep object to stack
						stack.push(taskStep);
						event = eventReader.nextEvent();
						continue;
					}
					if (event.asStartElement().getName().getLocalPart() == ("id")) {
						// Pop Task from stack
						TaskComposite task = (TaskComposite) stack.peek();
						// Set task title
						event = eventReader.nextEvent();
						task.setId(Integer.parseInt(event.asCharacters().getData()));
						continue;
					}

					if (event.asStartElement().getName().getLocalPart() == ("title")) {
						// Pop Task from stack
						TaskComposite task = (TaskComposite) stack.peek();
						// Set task title
						event = eventReader.nextEvent();
						task.setTitle(event.asCharacters().getData());
						continue;
					}

					if (event.asStartElement().getName().getLocalPart() == ("priority")) {
						// Pop Task from stack
						TaskComposite task = (TaskComposite) stack.peek();
						// Set task priority
						event = eventReader.nextEvent();
						task.setPriority(Integer.parseInt(event.asCharacters()
								.getData()));
						continue;
					}
					if (event.asStartElement().getName().getLocalPart() == ("isActive")) {
						// Pop Task from stack
						TaskComposite task = (TaskComposite) stack.peek();
						// Set task isActive
						event = eventReader.nextEvent();
						task.setIsActive(Integer.parseInt(event.asCharacters()
								.getData()));
						continue;
					}
					if (event.asStartElement().getName().getLocalPart() == ("deadline")) {
						event = eventReader.nextEvent();
						SimpleDateFormat sdf = new SimpleDateFormat(
								"yyMMddHHmmssZ");
						String s = event.asCharacters().getData().toString();
						Date date = sdf.parse(s);
						Calendar cal = Calendar.getInstance();
						cal.setTime(date);
						// Pop Task from stack
						TaskComposite task = (TaskComposite) stack.peek();
						// Set task deadline
						task.setDeadline(cal);
						continue;
					}
					if (event.asStartElement().getName().getLocalPart() == ("number")) {
						// Pop Task-step from stack
						TaskStep taskStep = (TaskStep) stack.peek();
						// Set task-step number
						event = eventReader.nextEvent();
						taskStep.setOrderNumber(Integer.parseInt(event
								.asCharacters().getData()));
						continue;
					}
					if (event.asStartElement().getName().getLocalPart() == ("description")) {
						try{
						// Pop Task-step from stack
						TaskStep taskStep = (TaskStep) stack.peek();
						// Set task deadline
						event = eventReader.nextEvent();
						taskStep.setDescription(event.asCharacters()
								.getData());
						}
						catch(ClassCastException ex){
							// Pop Task-step from stack
							TaskComposite taskComposite = (TaskComposite) stack.peek();
							// Set task description
							event = eventReader.nextEvent();
							taskComposite.setDescription(event.asCharacters()
									.getData());
						}
						continue;
					}
				}
				if (event.isEndElement()) {
					if (event.asEndElement().getName().getLocalPart() == ("task")) {
						// Pop Task object from stack
						if (stack.size() == 1) {
							stickyNoteList.addStickyNote((StickyNote) stack
									.pop());
						} else
							stack.pop();
						event = eventReader.nextEvent();
						continue;
					}
					if (event.asEndElement().getName().getLocalPart() == ("task-step")) {
						// Pop TaskStep object from stack
						stack.pop();
						event = eventReader.nextEvent();
						continue;
					}
				}
			}
			eventReader.close();
			in.close();
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (XMLStreamException e) {
			e.printStackTrace();
		}
	}

	public void writeXML() throws IOException {
		//Exceptions
		if (file == null) {
			throw new IllegalArgumentException("File should not be null.");
		}
		if (!file.exists()) {
			throw new FileNotFoundException("File does not exist: " + file);
		}
		if (!file.isFile()) {
			throw new IllegalArgumentException("Should not be a directory: "
					+ file);
		}
		if (!file.canWrite()) {
			throw new IllegalArgumentException("File cannot be written: "
					+ file);
		}

		//Format XML string by serializing StickyNote (TaskComposite and TaskStep) objects
		String xml = "<StickyNote>\n";
		if(stickyNoteList.getStickyNote().size()== 0){
			xml+="EMPTY";
		}
		for (StickyNote stickyNote : stickyNoteList.getStickyNote()) {
			xml += stickyNote.Serialiaze();
		}

		//Write to file
		Writer output = new BufferedWriter(new FileWriter(file));
		try 
		{
			output.write(xml + "</StickyNote>\n");
		} 
		finally 
		{
			output.close();
		}
	}

//	public static void main(String args[]) throws ParseException, IOException {
//		StorageAccess storageAccess = new StorageAccess();
//		storageAccess.setFile("StickyNotes.xml");
//		storageAccess.readXML();
//		storageAccess.writeXML();
//	}
}